using System;
using System.Data.Linq;
using System.Linq.Expressions;
using System.Reflection;
using PropertyExpression.Common;

namespace PropertyExpression.ReactiveRelation
{
    public static class EntityExtensions
    {
        public static ICalcInitiator<TEntity> Couple<TEntity>(
            this TEntity entities,
            ICalcExecutor calcExecutor)
        {
            return new CalcInitiator<TEntity>(entities, calcExecutor);
        }

        public static void AddChild<TEntity, TChildEntity>(
            this TEntity entity,
            Expression<Func<TEntity, EntitySet<TChildEntity>>> propertyExpression,
            ICalcInitiator<TChildEntity> childEntity) where TChildEntity : class
        {

            var reversePropertyInfo = propertyExpression.GetPropertyInfo().GetReversePropertyInfo();
            SetValue(
                reversePropertyInfo,
                entity,
                reversePropertyInfo.GetValue(childEntity.Entity, null),
                childEntity.Entity,
                childEntity.CalcExecutor
                );
        }

        private static void SetValue(
            PropertyInfo propertyInfo,
            object value,
            object previousValue,
            object sender,
            ICalcExecutor executor)
        {
            propertyInfo.SetValue(sender, value, null);

            executor.Calculate(
                sender,
                () => propertyInfo.Name,
                previousValue,
                () => new OneToManyCollectionNewItemProcessor(
                          OptionalResult.AsOptionalResult<IOneToManyCollectionNewItemProvider>)
                );
        }

        private class CalcInitiator<TEntity> : ICalcInitiator<TEntity>
        {
            private readonly TEntity entity;
            private readonly ICalcExecutor calcExecutor;

            public CalcInitiator(TEntity entity, ICalcExecutor calcExecutor)
            {
                this.entity = entity;
                this.calcExecutor = calcExecutor;
            }

            public ICalcExecutor CalcExecutor
            {
                get { return calcExecutor; }
            }

            public Func<TProperty, ICalcInitiator<TEntity>> GetValue<TProperty>(
                IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy)
            {
                return
                    value =>
                        {
                            //TODO: improve performance
                            SetValue(
                                propertyExpressionProxy.GetPropertyInfo(),
                                value,
                                //TODO: improve performance
                                propertyExpressionProxy.Execute(entity),
                                entity,
                                calcExecutor
                                );

                            return this;
                        };
            }

            public TEntity Entity
            {
                get { return entity; }
            }
        }
    }
}